// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose:  Implementation of CXFormsManager class..
//   $Id$


#include "stdafx.h"
#include "XFormsManager.h"
#include "XFormsApp.h"
#include "FP_Definitions.h"
#include "BoundNode.h"
#include "Model.h"
#include "Vertex.h"
#include "FP_ReturnCodes.h"
#include "XFormsElement.h"
//#include <Soft/Soft-Bar/Soft-Bar.h>
//#include <Soft/Soft-Bar/Soft-Bar_i.c>
#include "mustunderstand_i.c"
#include <atlstr.h>
#include "implInstance.h"
#include "mapBSTRToInstancePtr.h"
#include "nodebinding.h"
#include <Common/ResourceHandling/msw/loadHTMLResource.h>

#ifdef WAIT_FOR_DOMEVENT_WITH_MESSAGELOOP
	#include "WaitWithMessageLoop.h"
#endif

extern CXFormsApp theApp;

CXFormsManager::CXFormsManager()
: m_pos(0)
, m_bCheckedRequiredComponents(false)
, theErrorSource(0)
, m_hEvent(0)
, m_bInitialised(false)
//, m_nSystrayRefCount(0)
, m_bEnableEventNotifications(false)
{
}

CXFormsManager::~CXFormsManager()
{
	// Bug 386: By the time we reach this point, CoUninitialize() will already have freed any
	//          resources. Given this, any attempt by CComPtrBase::~CComPtrBase() to release
	//          its pointer member will cause a crash. This should never occur, due to the
	//          explicit calls to Release() that are in CXFormsManager::onExitInstance(). But,
	//          when formsPlayer is part of Focus XFormation, it has been seen that this
	//          method never gets called. Hence, the following assignments prevent a crash on
	//          shutting down Focus XFormation.
	m_pSchemaCache.p = 0;
	theUnderstander.p = 0;
	m_pExposedInfoDOM.p = 0;
//	m_pConfig.p = 0;
	m_pDIR.p = 0;
//	m_pDefaultSysTrayIcon.p = 0;
	m_Forms.RemoveAll();
}
HRESULT CXFormsManager::GetUnderstander(IUnderstander** ppUnderstander)
{
	HRESULT hr = E_FAIL;
	if(!theUnderstander)
	{
		hr  = theUnderstander.CoCreateInstance(CLSID_Understander);
		if(SUCCEEDED(hr))
			loadUnderstoodTags();
	}

	if(theUnderstander)
	{
		hr = theUnderstander.CopyTo(ppUnderstander);
	}
	return hr;

}


CXFormsXFormsElement* CXFormsManager::GetForms(bool bStart)
{
	void *pKey = 0;
	CXFormsXFormsElement *pForm = 0;
	if(bStart)
		m_pos = m_Forms.GetStartPosition();
	if(m_pos)
		m_Forms.GetNextAssoc(m_pos, (void*&)pKey, (void*&)pForm);
	return pForm;
}

// Returns a CXFormsXFormsElement, If the key already exists in m_Documents,
// it returns an already extant form, ohterwise, creates a new one.

HRESULT CXFormsManager::GetForm(LONG lKey, CXFormsXFormsElement **ppForm)
{
	HRESULT hr = S_FALSE;
	if(lKey)
	{
		LPVOID pForm = 0;
		m_Forms.Lookup(reinterpret_cast<void*>(lKey),pForm);
		if(pForm)
		{
			*ppForm = static_cast<CXFormsXFormsElement*>(pForm);
			hr = S_OK;
		}
	}
	return hr;
}

HRESULT CXFormsManager::GetForm(IHTMLDocument2 *pDoc2, CXFormsXFormsElement **ppForm)
{

	//This form has not yet been encountered
	//	create a new form for it.
	*ppForm = new CXFormsXFormsElement(pDoc2);
	HRESULT hr = S_OK;
	//Key should really be returned by this function, 
	// rather than relying on knowledge that the key is a  
	//	cast of the pointer.
	long lKey = reinterpret_cast<long>(*ppForm);
	m_Forms.SetAt(reinterpret_cast<void*>(lKey),*ppForm);
	(*ppForm)->m_lFormKey = lKey;
	return hr;
}



HRESULT CXFormsManager::getFormsDOM(IXMLDOMDocument **ppFormsDOM, bool bUseOmnipresentIds)
{
	HRESULT hr = S_OK;
	if(ppFormsDOM) {
		CComPtr<IXMLDOMNode> pFormsNode;
		hr = CreateAndInitialiseFormsDOM(&pFormsNode);
		if(SUCCEEDED(hr)) {
			CList<CXFormsXFormsElement*, CXFormsXFormsElement*> l;
			POSITION pos = 0;
			CXFormsXFormsElement *p = GetForms(true);
			while(p) {
				if(!l.Find(p)) {
					pos = l.AddTail(p);
					CComPtr<IXMLDOMElement> pEl;
					BSTR sFormTag = ::SysAllocString(g_lpwszTagForm);
					if(SUCCEEDED(m_pExposedInfoDOM->createElement(sFormTag, &pEl))) {
						InsertIdAttribute(pEl, p);
						InsertURLAttribute(pEl, p);
						InsertModelsAndInstancesIntoFormsDOM(pEl, p, bUseOmnipresentIds);
						CComQIPtr<IXMLDOMNode> pNQI = pEl;
						CComPtr<IXMLDOMNode> pNode;
						hr = pFormsNode->appendChild(pNQI, &pNode);
					}
					if(sFormTag)
						::SysFreeString(sFormTag);
				}
				p = GetForms();
			}
			*ppFormsDOM = m_pExposedInfoDOM.Detach();
		}
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CFactory::getForms");
	}
	return hr;
}

HRESULT CXFormsManager::getModel(BSTR sFormId, BSTR sModelId, IXFormsModelElement **ppModel, bool bUseOmnipresentIds)
{
	HRESULT hr = S_FALSE;
	if(sFormId && sModelId && ppModel) {
		CList<CXFormsXFormsElement*, CXFormsXFormsElement*> FormList;
		POSITION pos = 0;
		CStringW sMatchFormId;
		CXFormsXFormsElement *pForm = GetForms(true);
		while(pForm) {
			if(!FormList.Find(pForm)) {
				pos = FormList.AddTail(pForm);
				sMatchFormId.Format(L"%X", TwosComplement(reinterpret_cast<long>(pForm)));
				if(sMatchFormId == sFormId) {
					CMapStringToPtr *ModelList = pForm->getModelList();
					if(ModelList) {
						pos = ModelList->GetStartPosition();
						CString s;
						void *p = 0;
						while(pos) {
							ModelList->GetNextAssoc(pos, s, p);
							CImplModel *pModel = reinterpret_cast<CImplModel*>(p);
							if(pModel && pModel->m_pXFormsElement) {
								CComBSTR sUniqueId;
								sUniqueId.Attach(pModel->UniqueId());
								if((bUseOmnipresentIds && sUniqueId == sModelId) || (!bUseOmnipresentIds && pModel->m_sId == sModelId)) {
									hr = pModel->m_pXFormsElement->QueryInterface(IID_IXFormsModelElement, (void**)ppModel);
									break;
								}
							}
						}
					}
					break;
				}
			}
			pForm = GetForms();
		}
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CFactory::getModel");
	}
	return hr;
}

HRESULT CXFormsManager::getControlsDOM(BSTR sFormId, IXMLDOMDocument **ppControlsDOM, bool bUseOmnipresentIds)
{
	HRESULT hr = S_OK;
	if(sFormId && ppControlsDOM) {
		CList<CXFormsXFormsElement*, CXFormsXFormsElement*> l;
		POSITION pos = 0;
		CXFormsXFormsElement *p = GetForms(true);
		while(p) {
			if(!l.Find(p)) {
				pos = l.AddTail(p);
				CStringW s;
				s.Format(L"%X", TwosComplement(reinterpret_cast<long>(p)));
				if(s == sFormId) {
					CComPtr<IXMLDOMNode> pControlsNode;
					hr = CreateAndInitialiseControlsDOM(&pControlsNode);
					if(SUCCEEDED(hr)) {
						hr = PopulateControlsDOM(p, pControlsNode, bUseOmnipresentIds);
						if(SUCCEEDED(hr))
							*ppControlsDOM = m_pExposedInfoDOM.Detach();
						break;
					}
				}
			}
			p = GetForms();
		}
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CFactory::getControls");
	}
	return hr;
}

HRESULT CXFormsManager::getControlInformationDOM(BSTR sFormId, BSTR sControlId, IXMLDOMDocument **ppControlInformationDOM, bool bUseOmnipresentIds)
{
	HRESULT hr = S_OK;
	if(sFormId && sControlId && ppControlInformationDOM) {
		CList<CXFormsXFormsElement*, CXFormsXFormsElement*> l;
		POSITION pos = 0;
		CXFormsXFormsElement *p = GetForms(true);
		while(p) {
			if(!l.Find(p)) {
				pos = l.AddTail(p);
				CStringW s;
				s.Format(L"%X", TwosComplement(reinterpret_cast<long>(p)));
				if(s == sFormId) {
					const CFormControl *pFC = 0;
					hr = FindControl(sControlId, p, &pFC, bUseOmnipresentIds);
					if(hr == S_OK) {
						hr = CreateAndInitialiseControlInfoDOM(pFC, ppControlInformationDOM, bUseOmnipresentIds);
						break;
					}
				}
			}
			p = GetForms();
		}
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CFactory::getControlInformation");
	}
	return hr;
}

HRESULT CXFormsManager::CreateAndInitialiseFormsDOM(IXMLDOMNode **ppFormsNode)
{
	HRESULT hr = S_OK;
	if(ppFormsNode) {
		if(m_pExposedInfoDOM.p)
			m_pExposedInfoDOM.Release();
		m_pExposedInfoDOM.CoCreateInstance(CLSID_DOMDocument60);
		CComPtr<IXMLDOMElement> pElXFE;
		BSTR sXFETag = ::SysAllocString(g_lpwszTagXFE);
		hr = m_pExposedInfoDOM->createElement(sXFETag, &pElXFE);
		if(SUCCEEDED(hr)) {
			CComQIPtr<IXMLDOMNode> pQINode = pElXFE;
			CComPtr<IXMLDOMNode> pNode2;
			hr = m_pExposedInfoDOM->appendChild(pQINode, &pNode2);
			if(SUCCEEDED(hr)) {
				CComPtr<IXMLDOMElement> pElForms;
				BSTR sFormsTag = ::SysAllocString(g_lpwszTagForms);
				hr = m_pExposedInfoDOM->createElement(sFormsTag, &pElForms);
				if(SUCCEEDED(hr)) {
					CComQIPtr<IXMLDOMNode> pQINode2 = pElForms;
					hr = pNode2->appendChild(pQINode2, ppFormsNode);
				}
				if(sFormsTag)
					::SysFreeString(sFormsTag);
			}
		}
		if(sXFETag)
			::SysFreeString(sXFETag);
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CXFormsManager::CreateAndInitialiseFormsDOM");
	}
	return hr;
}

HRESULT CXFormsManager::InsertURLAttribute(IXMLDOMElement *pEl, CXFormsXFormsElement *pXXE)
{
	HRESULT hr = S_OK;
	if(pEl && pXXE && m_pExposedInfoDOM) {
		BSTR s = 0;
		CComPtr<IHTMLDocument4> pDoc;
		if(SUCCEEDED(pXXE->GetHostDocument(&pDoc)))
			pDoc->get_URLUnencoded(&s);
		VARIANT v;
		v.vt = VT_BSTR;
		v.bstrVal = ::SysAllocString(s ? s : g_lpwszUnknown);
		BSTR sURLAttr = ::SysAllocString(g_lpwszAttribURL);
		hr = pEl->setAttribute(sURLAttr, v);
		if(sURLAttr)
			::SysFreeString(sURLAttr);
		if(v.bstrVal)
			::SysFreeString(v.bstrVal);
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CXFormsManager::InsertURLAttribute");
	}
	return hr;
}

HRESULT CXFormsManager::InsertIdAttribute(IXMLDOMElement *pEl, CXFormsXFormsElement *pXXE, BSTR sId)
{
	HRESULT hr = S_OK;
	if(pEl && m_pExposedInfoDOM) {
		CStringW s;
		if(sId)
			s = sId;
		else
			s.Format(L"%X", TwosComplement(reinterpret_cast<long>(pXXE)));
		CComVariant v(s.GetString());
		CComBSTR sIdAttr = g_lpwszAttribId;
		hr = pEl->setAttribute(sIdAttr, v);
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CXFormsManager::InsertIdAttribute");
	}
	return hr;
}

HRESULT CXFormsManager::InsertSrcidAttribute(IXMLDOMElement *pEl, BSTR sId)
{
	HRESULT hr = S_OK;
	if(pEl && sId && m_pExposedInfoDOM) {
		CComVariant v(sId);
		CComBSTR sSrcidAttr = g_lpwszAttribSrcid;
		hr = pEl->setAttribute(sSrcidAttr, v);
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CXFormsManager::InsertIdAttribute");
	}
	return hr;
}

HRESULT CXFormsManager::InsertTypeAttribute(IXMLDOMElement *pEl, BSTR sType)
{
	HRESULT hr = S_OK;
	if(pEl && sType) {
		CComBSTR s = g_lpwszAttribType;
		CComVariant v(sType);
		hr = pEl->setAttribute(s, v);
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CXFormsManager::InsertIdAttribute");
	}
	return hr;
}

// *** TODO: MAKE THIS METHOD RECURSIVE RATHER THAN HAVING TWO NEAR-IDENTICAL LOOPS. ***
HRESULT CXFormsManager::InsertModelsAndInstancesIntoFormsDOM(IXMLDOMElement *pEl, CXFormsXFormsElement *pXXE, bool bUseOmnipresentIds)
{
	HRESULT hr = S_OK;
	if(pEl && pXXE && m_pExposedInfoDOM) {
		CMapStringToPtr *ModelList = pXXE->getModelList();
		if(ModelList) {
			POSITION pos = ModelList->GetStartPosition();
			CString s;
			void *p = 0;
			while(pos) {
				ModelList->GetNextAssoc(pos, s, p);
				CImplModel *pModel = reinterpret_cast<CImplModel*>(p);
				if(pModel) {
					CComPtr<IXMLDOMElement> pModelEl;
					CComBSTR bs = g_lpwszTagModel;
					hr = m_pExposedInfoDOM->createElement(bs, &pModelEl);
					if(SUCCEEDED(hr)) {
						if(bUseOmnipresentIds) {
							bs.Empty();
							bs.Attach(pModel->UniqueId());
							InsertSrcidAttribute(pModelEl, bs);
						} else
							bs = (pModel->m_sId.Length() > 0 ? pModel->m_sId : g_lpwszAnonymous);
						InsertIdAttribute(pModelEl, 0, bs);
						CComQIPtr<IXMLDOMNode> pNQI = pModelEl;
						CComPtr<IXMLDOMNode> pNode;
						hr = pEl->appendChild(pNQI, &pNode);
						if(SUCCEEDED(hr)) {
							CMapBSTRToInstancePtr *InstList = pModel->getInstanceList();
							if(InstList) {
								CMapBSTRToInstancePtr::iterator i = InstList->begin();
								while(i != InstList->end())
								{
									CImplInstance *pInst = i->second;
									++i;
									if(pInst) {
										CComPtr<IXMLDOMElement> pInstEl;
										bs = g_lpwszTagInstance;
										hr = m_pExposedInfoDOM->createElement(bs, &pInstEl);
										if(SUCCEEDED(hr)) {
											if(bUseOmnipresentIds) {
												bs.Empty();
												bs.Attach(pInst->UniqueId());
												InsertSrcidAttribute(pInstEl, bs);
											} else
												bs = (pInst->m_sId.Length() > 0 ? pInst->m_sId : g_lpwszAnonymous);
											InsertIdAttribute(pInstEl, 0, bs);
											CComQIPtr<IXMLDOMNode> pNQI2 = pInstEl;
											CComPtr<IXMLDOMNode> pNode2;
											hr = pNode->appendChild(pNQI2, &pNode2);
										}
									}
								}
							}
						}
					}
				}
			}
		}
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CXFormsManager::InsertModelsAndInstancesIntoFormsDOM");
	}
	return hr;
}

HRESULT CXFormsManager::CreateAndInitialiseControlsDOM(IXMLDOMNode **ppControlsNode)
{
	HRESULT hr = S_OK;
	if(ppControlsNode) {
		if(m_pExposedInfoDOM.p)
			m_pExposedInfoDOM.Release();
		m_pExposedInfoDOM.CoCreateInstance(CLSID_DOMDocument60);
		CComPtr<IXMLDOMElement> pEl;
		BSTR sXFETag = ::SysAllocString(g_lpwszTagXFE);
		hr = m_pExposedInfoDOM->createElement(sXFETag, &pEl);
		if(SUCCEEDED(hr)) {
			CComQIPtr<IXMLDOMNode> pQINode = pEl;
			CComPtr<IXMLDOMNode> pNode2;
			hr = m_pExposedInfoDOM->appendChild(pQINode, &pNode2);
			if(SUCCEEDED(hr)) {
				CComPtr<IXMLDOMElement> pEl2;
				BSTR sCtrlsTag = ::SysAllocString(g_lpwszTagControls);
				hr = m_pExposedInfoDOM->createElement(sCtrlsTag, &pEl2);
				if(SUCCEEDED(hr)) {
					CComQIPtr<IXMLDOMNode> pQINode2 = pEl2;
					hr = pNode2->appendChild(pQINode2, ppControlsNode);
				}
				if(sCtrlsTag)
					::SysFreeString(sCtrlsTag);
			}
		}
		if(sXFETag)
			::SysFreeString(sXFETag);
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CXFormsManager::CreateControlsDOM");
	}
	return hr;
}

HRESULT CXFormsManager::PopulateControlsDOM(CXFormsXFormsElement *pXFXFE, IXMLDOMNode *pControlsNode, bool bUseOmnipresentIds)
{
	HRESULT hr = S_OK;
	// Check the arguments.
	if(pXFXFE && pControlsNode && m_pExposedInfoDOM) {
		const CFormControl *pFC = 0;
		// Get the first form control...
		hr = pXFXFE->GetControl(&pFC, true);
		// ...then loop through each in turn.
		while(hr == S_OK) {
			CComPtr<IXMLDOMElement> pElCtrl;
			CComBSTR bs = g_lpwszControl;
			// Create the element representing this form control.
			hr = m_pExposedInfoDOM->createElement(bs, &pElCtrl);
			if(SUCCEEDED(hr)) {
				try {
					bs.Empty();
					// Get the control's XForms id.
					bs = (pFC->m_sId.Length() > 0 ? pFC->m_sId : g_lpwszAnonymous);
					if(bUseOmnipresentIds) {
						// For omnipresent identifiers, set this as the 'srcid' attribute...
						InsertSrcidAttribute(pElCtrl, bs);
						// ...then get the omnipresent identifier.
						bs.Empty();
						bs.Attach(pFC->UniqueId());
					}
					// Set this as the 'id' attribute.
					InsertIdAttribute(pElCtrl, 0, bs);

					// Set the control type attribute
					bs.Empty();
					bs = (pFC->GetControlType() ? pFC->GetControlType() : g_lpwszUnknown);
					InsertTypeAttribute(pElCtrl, bs);

					CComQIPtr<IXMLDOMNode> pQINode = pElCtrl;
					if(pQINode) {
						CComPtr<IXMLDOMNode> pNodeCtrl;
						// Append the information to the DOM.
						hr = pControlsNode->appendChild(pQINode, &pNodeCtrl);
					}
				}
				catch(...) { ASSERT(0); /* BAD POINTER IN THE LIST OF CONTROLS ??? */ }
			}
			// Get the next form control.
			hr = pXFXFE->GetControl(&pFC);
		}
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CXFormsManager::PopulateControlsDOM");
	}
	return hr;
}

HRESULT CXFormsManager::FindControl(BSTR sControlId, CXFormsXFormsElement *pXFXFE, const CFormControl **ppFC, bool bUseOmnipresentIds)
{
	HRESULT hr = S_FALSE;
	if(sControlId && pXFXFE && ppFC) {
		const CFormControl *pFC = 0;
		hr = pXFXFE->GetControl(&pFC, true);
		while(hr == S_OK) {
			try {
				CComBSTR s;
				if(bUseOmnipresentIds)
					s.Attach(pFC->UniqueId());
				else
					s = pFC->m_sId;
				if(s == sControlId) {
					*ppFC = pFC;
					hr = S_OK;
					break;
				}
			}
			catch(...) { ASSERT(0); /* BAD POINTER IN THE LIST OF CONTROLS ??? */ }
			hr = pXFXFE->GetControl(&pFC);
		}
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CXFormsManager::FindControl");
	}
	return hr;
}

HRESULT CXFormsManager::CreateAndInitialiseControlInfoDOM(const CFormControl *pFC, IXMLDOMDocument **ppXDE, bool bUseOmnipresentIds)
{
	HRESULT hr = S_OK;
	if(pFC && ppXDE) {
		if(m_pExposedInfoDOM.p)
			m_pExposedInfoDOM.Release();
		m_pExposedInfoDOM.CoCreateInstance(CLSID_DOMDocument60);
		CComPtr<IXMLDOMElement> pElXFE;
		CComBSTR s = g_lpwszTagXFE;
		hr = m_pExposedInfoDOM->createElement(s, &pElXFE);
		if(SUCCEEDED(hr)) {
			CComQIPtr<IXMLDOMNode> pQINode = pElXFE;
			CComPtr<IXMLDOMNode> pNodeXFE;
			hr = m_pExposedInfoDOM->appendChild(pQINode, &pNodeXFE);
			if(SUCCEEDED(hr)) {
				CComPtr<IXMLDOMElement> pElCtrl;
				s = g_lpwszControl;
				hr = m_pExposedInfoDOM->createElement(s, &pElCtrl);
				if(SUCCEEDED(hr)) {
					s = (pFC->GetControlType() ? pFC->GetControlType() : g_lpwszUnknown);
					InsertTypeAttribute(pElCtrl, s);
					if(bUseOmnipresentIds) {
						s.Empty();
						s.Attach(pFC->UniqueId());
						InsertSrcidAttribute(pElCtrl, s);
					} else
						s = (pFC->m_sId.Length() > 0 ? pFC->m_sId : g_lpwszAnonymous);
					InsertIdAttribute(pElCtrl, 0, s);
					CComQIPtr<IXMLDOMNode> pQINode2 = pElCtrl;
					CComPtr<IXMLDOMNode> pNodeCtrl;
					hr = pNodeXFE->appendChild(pQINode2, &pNodeCtrl);
					if(SUCCEEDED(hr)) {
						const CImplNodeBinding *pINB = dynamic_cast<const CImplNodeBinding*>(pFC);
						if(pINB) {
							CreateModelForControlInfoDOM(pNodeCtrl, pINB);
							CreateRefOrNodesetForControlInfoDOM(pNodeCtrl, pINB);
							//TODO:
							//CreateFullXPathForControlInfoDOM(pNodeCtrl, pINB);
							CreateModelItemPropertiesForControlInfoDOM(pNodeCtrl, pINB);
							*ppXDE = m_pExposedInfoDOM.Detach();
						}
					}
				}
			}
		}
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CXFormsManager:CreateControlInfoDOM");
	}
	return hr;
}

HRESULT CXFormsManager::CreateModelForControlInfoDOM(IXMLDOMNode *pNodeCtrl, const CImplNodeBinding *pINB)
{
	HRESULT hr = S_OK;
	if(pNodeCtrl && pINB && m_pExposedInfoDOM) {
		CComPtr<IXMLDOMElement> pElMdl;
		BSTR sModelTag = ::SysAllocString(g_lpwszTagModel);
		hr = m_pExposedInfoDOM->createElement(sModelTag, &pElMdl);
		if(SUCCEEDED(hr)) {
			CComQIPtr<IXMLDOMNode> pQINode = pElMdl;
			CComPtr<IXMLDOMNode> pNodeMdl;
			hr = pNodeCtrl->appendChild(pQINode, &pNodeMdl);
			if(SUCCEEDED(hr)) {
				CComPtr<IXMLDOMText> pNodeText;
				CStringW s;
				if(pINB->m_pModel && pINB->m_pModel->m_sId)
					s = pINB->m_pModel->m_sId;
				else
					s = g_lpwszUnknown;
				BSTR bs = s.AllocSysString();
				hr = m_pExposedInfoDOM->createTextNode(bs, &pNodeText);
				::SysFreeString(bs);
				if(SUCCEEDED(hr)) {
					CComQIPtr<IXMLDOMNode> pQINode2 = pNodeText;
					CComPtr<IXMLDOMNode> pNodeTxt;
					hr = pNodeMdl->appendChild(pQINode2, &pNodeTxt);
				}
			}
		}
		if(sModelTag)
			::SysFreeString(sModelTag);
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CXFormsManager::CreateModelForInfoDOM");
	}
	return hr;
}

HRESULT CXFormsManager::CreateRefOrNodesetForControlInfoDOM(IXMLDOMNode *pNodeCtrl, const CImplNodeBinding *pINB)
{
	HRESULT hr = S_OK;
	if(pNodeCtrl && pINB && m_pExposedInfoDOM) {
		CComPtr<IXMLDOMElement> pElRefOrNodeset;
		CComBSTR s = g_lpwszRefOrNodeset;
		hr = m_pExposedInfoDOM->createElement(s, &pElRefOrNodeset);
		if(SUCCEEDED(hr)) {
			s = (pINB->m_bApplyFirstNodeRule ? g_lpwszRef : g_lpwszNodeset);
			InsertTypeAttribute(pElRefOrNodeset, s);
			if(SUCCEEDED(hr)) {
				CComQIPtr<IXMLDOMNode> pQINode = pElRefOrNodeset;
				CComPtr<IXMLDOMNode> pNodeRefOrNodeset;
				hr = pNodeCtrl->appendChild(pQINode, &pNodeRefOrNodeset);
				if(SUCCEEDED(hr)) {
					CComPtr<IXMLDOMText> pNodeText;
					s = (pINB->m_pbsXPathExpression ? *pINB->m_pbsXPathExpression : g_lpwszUnknown);
					hr = m_pExposedInfoDOM->createTextNode(s, &pNodeText);
					if(SUCCEEDED(hr)) {
						CComQIPtr<IXMLDOMNode> pQINode2 = pNodeText;
						CComPtr<IXMLDOMNode> pNodeTxt;
						hr = pNodeRefOrNodeset->appendChild(pQINode2, &pNodeTxt);
					}
				}
			}
		}
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CXFormsManager::CreateRefForControlInfoDOM");
	}
	return hr;
}

HRESULT CXFormsManager::CreateFullXPathForControlInfoDOM(IXMLDOMNode *pNodeCtrl, const CImplNodeBinding *pINB)
{
	HRESULT hr = S_OK;
	if(pNodeCtrl && pINB && m_pExposedInfoDOM) {
		CComPtr<IXMLDOMElement> pElFullXPath;
		BSTR sFullXPTag = ::SysAllocString(g_lpwszFullXPath);
		hr = m_pExposedInfoDOM->createElement(sFullXPTag, &pElFullXPath);
		if(SUCCEEDED(hr)) {
			CComQIPtr<IXMLDOMNode> pQINode = pElFullXPath;
			CComPtr<IXMLDOMNode> pNodeFullXPath;
			hr = pNodeCtrl->appendChild(pQINode, &pNodeFullXPath);
			if(SUCCEEDED(hr)) {
				CComPtr<IXMLDOMText> pNodeText;
				// Still need to write pINB->getFullXPath().
				CComBSTR sFullXPath;
				sFullXPath.Attach(pINB->getFullXPath());
				hr = m_pExposedInfoDOM->createTextNode(sFullXPath, &pNodeText);
				if(SUCCEEDED(hr)) {
					CComQIPtr<IXMLDOMNode> pQINode2 = pNodeText;
					CComPtr<IXMLDOMNode> pNodeTxt;
					hr = pNodeFullXPath->appendChild(pQINode2, &pNodeTxt);
				}
			}
		}
		if(sFullXPTag)
			::SysFreeString(sFullXPTag);
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CXFormsManager::CreateFullXPathForControlInfoDOM");
	}
	return hr;
}

HRESULT CXFormsManager::CreateModelItemPropertiesForControlInfoDOM(IXMLDOMNode *pNodeCtrl, const CImplNodeBinding *pINB)
{
	HRESULT hr = S_OK;
	if(pNodeCtrl && pINB && m_pExposedInfoDOM) {
		bool bReadOnly = false, bValid = true, bRelevant = true;
		if(pINB->m_vertices.GetUpperBound() != -1) {
			CBoundNode *pBN = pINB->m_vertices[0]->m_pInstanceNode;
			bReadOnly = pBN->isReadonly();
			bValid = pBN->isValid();
			bRelevant = pBN->isRelevant();
		}
		CComPtr<IXMLDOMElement> pElReadOnly;
		BSTR sRdOnlyTag = ::SysAllocString(g_lpwszReadOnly);
		hr = m_pExposedInfoDOM->createElement(sRdOnlyTag, &pElReadOnly);
		if(SUCCEEDED(hr)) {
			VARIANT v;
			v.vt = VT_BSTR;
			v.bstrVal = ::SysAllocString(bReadOnly ? g_lpwszTrue : g_lpwszFalse);
			BSTR sStateAttr = ::SysAllocString(g_lpwszState);
			hr = pElReadOnly->setAttribute(sStateAttr, v);
			if(SUCCEEDED(hr)) {
				CComQIPtr<IXMLDOMNode> pQINode = pElReadOnly;
				CComPtr<IXMLDOMNode> pNodeReadOnly;
				hr = pNodeCtrl->appendChild(pQINode, &pNodeReadOnly);
			}
			if(sStateAttr)
				::SysFreeString(sStateAttr);
			if(v.bstrVal)
				::SysFreeString(v.bstrVal);
		}
		if(sRdOnlyTag)
			::SysFreeString(sRdOnlyTag);
		CComPtr<IXMLDOMElement> pElValid;
		BSTR sValidTag = ::SysAllocString(g_lpwszValid);
		hr = m_pExposedInfoDOM->createElement(sValidTag, &pElValid);
		if(SUCCEEDED(hr)) {
			VARIANT v;
			v.vt = VT_BSTR;
			v.bstrVal = ::SysAllocString(bValid?g_lpwszTrue:g_lpwszFalse);
			BSTR sStateAttr = ::SysAllocString(g_lpwszState);
			hr = pElValid->setAttribute(sStateAttr, v);
			if(SUCCEEDED(hr)) {
				CComQIPtr<IXMLDOMNode> pQINode = pElValid;
				CComPtr<IXMLDOMNode> pNodeValid;
				hr = pNodeCtrl->appendChild(pQINode, &pNodeValid);
			}
			if(sStateAttr)
				::SysFreeString(sStateAttr);
			if(v.bstrVal)
				::SysFreeString(v.bstrVal);
		}
		if(sValidTag)
			::SysFreeString(sValidTag);
		CComPtr<IXMLDOMElement> pElRelevant;
		BSTR sRelTag = ::SysAllocString(g_lpwszRelevant);
		hr = m_pExposedInfoDOM->createElement(sRelTag, &pElRelevant);
		if(SUCCEEDED(hr)) {
			VARIANT v;
			v.vt = VT_BSTR;
			v.bstrVal = ::SysAllocString(bRelevant?g_lpwszTrue:g_lpwszFalse);
			BSTR sStateAttr = ::SysAllocString(g_lpwszState);
			hr = pElRelevant->setAttribute(sStateAttr, v);
			if(SUCCEEDED(hr)) {
				CComQIPtr<IXMLDOMNode> pQINode = pElRelevant;
				CComPtr<IXMLDOMNode> pNodeRelevant;
				hr = pNodeCtrl->appendChild(pQINode, &pNodeRelevant);
			}
			if(sStateAttr)
				::SysFreeString(sStateAttr);
			if(v.bstrVal)
				::SysFreeString(v.bstrVal);
		}
		if(sRelTag)
			::SysFreeString(sRelTag);
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CXFormsManager::CreateModelItemPropertiesForControlInfoDOM");
	}
	return hr;
}

HRESULT CXFormsManager::canUnderstandTag(BSTR bsNamespace,BSTR bsTagName,BOOL * pbCanUnderStand)
{
	CComPtr<IUnderstander> pUnderstander;
	HRESULT hr = GetUnderstander(&pUnderstander);
	if(pUnderstander)
	{
		hr = pUnderstander->Understands(bsNamespace,bsTagName,pbCanUnderStand);
	}
	return hr;
}

void CXFormsManager::loadUnderstoodTags(void)
{
	CComPtr<INamespace> pNS;
	theUnderstander->GetNamespace(theApp.m_sXFormsURI,&pNS);
	if(pNS)
		AddKnownTagsToNamespace(pNS);

	// HACK: Use this namespace to circumnavigate the Novell XForms implementation.
	//CComPtr<INamespace> pNS_NovellHack;
	//theUnderstander->GetNamespace(theApp.m_sXFormsURI_NovellHack,&pNS_NovellHack);
	//if(pNS_NovellHack)
	//	AddKnownTagsToNamespace(pNS_NovellHack);
}

void CXFormsManager::AddKnownTagsToNamespace(INamespace *pNS)
{
	if(pNS) {
		pNS->AddElement(::SysAllocString(g_lpwszTagTrigger));
		pNS->AddElement(::SysAllocString(g_lpwszTagInput));
		pNS->AddElement(::SysAllocString(g_lpwszTagSecret));
		pNS->AddElement(::SysAllocString(g_lpwszTagTextArea));
		pNS->AddElement(::SysAllocString(g_lpwszTagSelectMany));
		pNS->AddElement(::SysAllocString(g_lpwszTagSelectOne));
		pNS->AddElement(::SysAllocString(g_lpwszTagRange));
		pNS->AddElement(::SysAllocString(g_lpwszTagOutput));
		pNS->AddElement(::SysAllocString(g_lpwszTagUpload));
		pNS->AddElement(::SysAllocString(g_lpwszTagSubmit));

		//---------------Head--------------
		pNS->AddElement(::SysAllocString(g_lpwszTagModel));
		pNS->AddElement(::SysAllocString(g_lpwszTagInstance));
		pNS->AddElement(::SysAllocString(g_lpwszTagBind));
		pNS->AddElement(::SysAllocString(g_lpwszTagSubmission));
				//NB: not in XForms namespace
//		pNS->AddElement(::SysAllocString(g_lpwszTagSchema));

		//------------common children
		pNS->AddElement(::SysAllocString(g_lpwszTagLabel));
		pNS->AddElement(::SysAllocString(g_lpwszTagHint));
		pNS->AddElement(::SysAllocString(g_lpwszTagHelp));
		pNS->AddElement(::SysAllocString(g_lpwszTagAlert));
		pNS->AddElement(::SysAllocString(g_lpwszTagExtension));

		//-----------containers
		pNS->AddElement(::SysAllocString(g_lpwszTagGroup));
		pNS->AddElement(::SysAllocString(g_lpwszTagRepeat));
		pNS->AddElement(::SysAllocString(g_lpwszTagSwitch));
		pNS->AddElement(::SysAllocString(g_lpwszTagCase));

		//-----------select children
		pNS->AddElement(::SysAllocString(g_lpwszTagItem));
		pNS->AddElement(::SysAllocString(g_lpwszTagItemset));
		pNS->AddElement(::SysAllocString(g_lpwszTagChoices));
		pNS->AddElement(::SysAllocString(g_lpwszTagValue));
		pNS->AddElement(::SysAllocString(g_lpwszTagCopy));


		//----upload children
		pNS->AddElement(::SysAllocString(g_lpwszTagFilename));
		pNS->AddElement(::SysAllocString(g_lpwszTagMediatype));

		//----Actions
		pNS->AddElement(::SysAllocString(g_lpwszTagAction));
		pNS->AddElement(::SysAllocString(g_lpwszTagDelete));
		pNS->AddElement(::SysAllocString(g_lpwszTagDispatch));
		pNS->AddElement(::SysAllocString(g_lpwszTagInsert));
		pNS->AddElement(::SysAllocString(g_lpwszTagLoad));
		pNS->AddElement(::SysAllocString(g_lpwszTagMessage));
		pNS->AddElement(::SysAllocString(g_lpwszTagRebuild));
		pNS->AddElement(::SysAllocString(g_lpwszTagRecalculate));
		pNS->AddElement(::SysAllocString(g_lpwszTagRefresh));
		pNS->AddElement(::SysAllocString(g_lpwszTagRevalidate));
		pNS->AddElement(::SysAllocString(g_lpwszTagReset));
		pNS->AddElement(::SysAllocString(g_lpwszTagSend));
		pNS->AddElement(::SysAllocString(g_lpwszTagSetFocus));
		pNS->AddElement(::SysAllocString(g_lpwszTagSetValue));
		pNS->AddElement(::SysAllocString(g_lpwszTagToggle));
		pNS->AddElement(::SysAllocString(g_lpwszTagSetIndex));
	}
}

/*

HRESULT CXFormsManager::InvokeOrRevokeSoftbar(CXFormsXFormsElement *pXFXFEl, BSTR sSoftbarName, bool bInvoke, bool bNameIsId)
{
	HRESULT hr = S_OK;
	// Check the arguments.
	if(pXFXFEl && sSoftbarName) {
		// First get a document pointer...
		CComPtr<IHTMLDocument2> pDoc;
		pXFXFEl->GetOwnerDocument(&pDoc);
		// ...then use that to get a pointer to the IWebBrowser2 interface.
		CComPtr<IWebBrowser2> pBrowser;
		hr = GetBrowserFromDocument(pDoc, &pBrowser);
		if(pBrowser) {
			// Now get a Soft-Bar object...
			IUnknown *pUnk = 0;
			xport::GetFirstImplementation(CComBSTR(L"fp.softbar"), CComBSTR(L"1.0"), &pUnk, &m_pDIR.p);
			CComQIPtr<ISoftBar> pSoftbar = pUnk;
			if(pSoftbar) {
				// ...and query it for our softbar id...
				CComBSTR sSoftbarId;
				hr = pSoftbar->QueryId(sSoftbarName, &sSoftbarId);
				if(SUCCEEDED(hr) || bNameIsId) {
					// ...then tell the web browser to invoke or revoke the softbar with that id.
					CComVariant vName = bNameIsId ? sSoftbarName : sSoftbarId;
					CComVariant vShow = bInvoke;
					CComVariant vReserved;
					hr = pBrowser->ShowBrowserBar(&vName, &vShow, &vReserved);
					if(FAILED(hr)) {
						// If ShowBrowserBar() failed, get document.external.maindoc and try again.
						CComPtr<IHTMLWindow2> pWin;
						pDoc->get_parentWindow(&pWin);
						CComPtr<IDispatch> pDisp;
						pWin->get_external(&pDisp);
						if(pDisp) {
							wchar_t *sMainDoc = L"maindoc";
							DISPID dispidMainDoc = 0;
							hr = pDisp->GetIDsOfNames(IID_NULL, &sMainDoc, 1, LOCALE_SYSTEM_DEFAULT, &dispidMainDoc);
							if(SUCCEEDED(hr)) {
								DISPPARAMS params;
								params.cArgs = params.cNamedArgs = 0;
								params.rgdispidNamedArgs = 0;
								params.rgvarg = 0;
								CComVariant vResult;
								EXCEPINFO eInfo;
								UINT nArgErr;
								pDisp->Invoke(dispidMainDoc, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params, &vResult, &eInfo, &nArgErr);
								if(vResult.vt == VT_DISPATCH && vResult.pdispVal) {
									CComQIPtr<IHTMLDocument2> pDoc2 = vResult.pdispVal;
									CComPtr<IWebBrowser2> pBrowser2;
									hr = GetBrowserFromDocument(pDoc2, &pBrowser2);
									if(pBrowser2)
										hr = pBrowser2->ShowBrowserBar(&vName, &vShow, &vReserved);
								}
							}
						}
					}
				}
				
			} else {
				hr = FP_E_NOCOM;
				SetupErrorInfo(hr, L"Soft-Bar");
			}
			if(pUnk)
				pUnk->Release();
		} else {
			hr = FP_E_BADARG;
			SetupErrorInfo(hr, L"XFormsManager::InvokeSoftbar");
		}
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"XFormsManager::InvokeSoftbar");
	}
	return hr;
}

*/

HRESULT CXFormsManager::TestForRequiredComponents(bool bForce)
{
	HRESULT hr = S_OK;
	if(bForce || !m_bCheckedRequiredComponents) {
		m_bCheckedRequiredComponents = true;
		IUnknown *pUnk = 0;
		xport::GetFirstImplementation(CComBSTR(L"xml"), CComBSTR(L"1.0"), &pUnk, &m_pDIR.p);
		CComQIPtr<IXMLDOMDocument> pDoc = pUnk;
		if(!pDoc) {
			// HACK: The new CErrorInfoHelper class uses MSXML6, so is unable to handle this error!
			//hr = FP_E_NOCOM;
			//SetupErrorInfo(hr, sFeature);
			hr = E_FAIL;
			theApp.m_pXForms->reportException("Missing XML parser: formsPlayer requires MSXM6 to be installed before it can function correctly. Please download MSXML4 from <a href=\"http://download.microsoft.com/download/9/6/5/9657c01e-107f-409c-baac-7d249561629c/msxml.msi\">here</a> before attempting to use formsPlayer.");
		}
		if(pUnk)
			pUnk->Release();
	}
	return hr;
}

HRESULT CXFormsManager::GetNextFeature(BSTR &sFeatures, BSTR &sFeature, BSTR &sVersion)
{
	HRESULT hr = S_OK;
	// First try to get the feature string
	wchar_t *p = wcschr(sFeatures, L' ');
	if(p) {
		// We have a token
		*p = L'\0';
		int nLen = wcslen(sFeatures);
		wchar_t *tmp = new wchar_t[nLen + 1];
		if(tmp) {
			wcscpy(tmp, sFeatures);
			sFeature = ::SysAllocString(tmp);
			if(!sFeature) {
				hr = FP_E_NOMEM;
				SetupErrorInfo(hr, L"CXFormsApp::GetNextFeature");
			}
			delete[] tmp;
		} else {
			hr = FP_E_NOMEM;
			SetupErrorInfo(hr, L"CXFormsApp::GetNextFeature");
		}
		*p = L' ';
		if(static_cast<size_t>(nLen + 1) < wcslen(sFeatures)) {
			// There are more tokens in the string
			BSTR sBuf = sFeatures;
			sFeatures = ::SysAllocString(p + 1);
			::SysFreeString(sBuf);
			if(SUCCEEDED(hr) && *sFeatures >= L'0' && *sFeatures <= L'9') {
				// There is also a version string to return
				p = wcschr(sFeatures, L' ');
				if(p) {
					// We have a token
					*p = L'\0';
					nLen = wcslen(sFeatures);
					tmp = new wchar_t[nLen + 1];
					if(tmp) {
						wcscpy(tmp, sFeatures);
						sVersion = ::SysAllocString(tmp);
						if(!sVersion) {
							hr = FP_E_NOMEM;
							SetupErrorInfo(hr, L"CXFormsApp::GetNextFeature");
						}
						delete[] tmp;
					} else {
						hr = FP_E_NOMEM;
						SetupErrorInfo(hr, L"CXFormsApp::GetNextFeature");
					}
					*p = L' ';
					if(static_cast<size_t>(nLen + 1) < wcslen(sFeatures)) {
						// There are more tokens in the string
						sBuf = sFeatures;
						sFeatures = ::SysAllocString(p + 1);
						::SysFreeString(sBuf);
					} else // No more tokens
						hr = FP_S_NOTOK;
				} else {
					// Version is last token of string
					sVersion = ::SysAllocString(sFeatures);
					hr = FP_S_NOTOK;
				}
			}
		} else // No more tokens
			hr = FP_S_NOTOK;
	} else {
		// Caller already has last token
		sFeature = sFeatures;
		hr = FP_S_NOTOK;
	}
	return hr;
}
#include "DOM3Core.h"
#include "MSHTMLDOMPurger.h"
HANDLE hGlobalDomEventFactoryCreatedEvent = 0; 

// called when the app receives ExitInstance
void CXFormsManager::onExitInstance(void)
{
	if(m_Forms.IsEmpty())
	{
		//These are only set to zero because of bug 256
		delete theErrorSource;
		theErrorSource = 0;
		CDOM3Document::DestroyFactory();
		// Bug 335: These must be released now, since by the time the destructor
		//          is called COM has been uninitialised.
		theUnderstander.Release();
		m_pDIR.Release();
//		m_pConfig.Release();
//		m_pDefaultSysTrayIcon.Release();
		m_pDOMEventFactory.Release();
		m_pExposedInfoDOM.Release();
		m_pSchemaCache.Release();
		
		try {
			CBoundNode::ClearStoredNodes();
			CMSHTMLDOMPurger::Clear();
		}
		catch(...) {
		}
	}


}



// loads a schema each for xsd and xforms datatypes
void CXFormsManager::loadGlobalSchemaCache(void)
{
	//Create the schemaCache
	HRESULT hr = S_FALSE;
	if(!m_pSchemaCache)
	{
			hr = CoCreateInstance
			(	
				CLSID_XMLSchemaCache60,
				NULL,
				CLSCTX_INPROC_SERVER,
				IID_IXMLDOMSchemaCollection2,
				(void**)&m_pSchemaCache
			);
	}

	// Load the XForms schema
	CComBSTR sResXML;
	xport::loadHTMLResource(::_Module.m_hInst,"xformsTypes.xsd", sResXML);
	AddToSchemaCache(sResXML, theApp.m_sXFormsURI);
	sResXML.Empty();

	// As above, but with the hacked namespace used to circumnavigate the Novell XForms implementation.
	//::loadHTMLResource("xformsTypesNovellHack.xsd", sResXML);
	//AddToSchemaCache(sResXML, theApp.m_sXFormsURI_NovellHack);
	//sResXML.Empty();

	// Load the schema data types schema.
	xport::loadHTMLResource(::_Module.m_hInst,"xsdDataTypes.xsd", sResXML);
	AddToSchemaCache(sResXML, CComBSTR(L"http://www.w3.org/2001/XMLSchema"));
	sResXML.Empty();
}

HRESULT CXFormsManager::AddToSchemaCache(BSTR sSchema, BSTR sNamespace)
{
	CDOM3Document doc;
	VARIANT_BOOL bLoaded = VARIANT_FALSE;
	HRESULT hr = doc.m_pXMLParser->loadXML(sSchema, &bLoaded);
	if(SUCCEEDED(hr) && bLoaded == VARIANT_TRUE) {
		CComVariant vtDoc(doc.m_pXMLParser);
		doc.m_pXMLParser->put_resolveExternals(VARIANT_TRUE);
		hr = m_pSchemaCache->add(sNamespace, vtDoc);
		if(FAILED(hr)) {
			IErrorInfo *pErr = 0;
			::GetErrorInfo(0, &pErr);
			CComBSTR sReason;
			if(pErr)
				pErr->GetDescription(&sReason);
			::safeRelease(pErr);

			::AtlTrace("could not load datatypes schema with namespace '%S'. Reason: '%S'.", sNamespace, sReason);
		}
	} 
	return hr;
}

HRESULT CXFormsManager::GetSchemaCache(IXMLDOMSchemaCollection** ppSchemaCache)
{
	HRESULT hr = E_FAIL;
	if(!m_pSchemaCache)
	{
		//TODO: this could be performed async on startup.
		//	If the global schema cache is loaded in a separate thread
		//	on startup, making the call to GetSchemaCache wait until
		//	that thread has exited, we would gain performance.
		loadGlobalSchemaCache();
	}
	
	if(m_pSchemaCache)
	{
		m_pSchemaCache.CopyTo(ppSchemaCache);
		hr = S_OK;
	}
	return hr;
}

// Register formsPlayer for writing to the event log, if not already done so.
HRESULT CXFormsManager::RegisterEventLog(void)
{
	HRESULT hr = S_OK;
	if(m_hEvent == 0) {
		HKEY hKey;
		if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\formsPlayer", 0, KEY_SET_VALUE, &hKey) == ERROR_SUCCESS) {
			char lpszValue[MAX_PATH];
			DWORD dw = ::GetModuleFileName(_Module.m_hInst, lpszValue, MAX_PATH);
			lpszValue[dw] = '\0';
			if(RegSetValueEx(hKey, "EventMessageFile", 0, REG_SZ, reinterpret_cast<BYTE *>(lpszValue), sizeof(char) * (dw + 1)) == ERROR_SUCCESS) {
				m_hEvent = RegisterEventSource(0, "formsPlayer");
				if(m_hEvent == 0) {
					hr = FP_E_REGEVT;
					SetupErrorInfo(hr);
				}
			} else {
				hr = FP_E_REGSET;
				SetupErrorInfo(hr, L"EventMessageFile");
			}
		} else {
			hr = FP_E_REGOPEN;
			SetupErrorInfo(hr, L"HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\formsPlayer");
		}
	} else
		hr = S_FALSE;
	return hr;
}

// Write to the event log.
HRESULT CXFormsManager::WriteEvent(WORD wType, DWORD dwEventId) const
{
	HRESULT hr = S_OK;
	if(ReportEvent(m_hEvent, wType, 0, dwEventId, 0, 0, 0, 0, 0) == 0)
		hr = E_FAIL;
	return hr;
}

// Return the directory that formsPlayer is installed in.
HRESULT CXFormsManager::GetFPHomeDirectory(BSTR *lpbsFPHome) const
{
	HRESULT hr = S_OK;
	if(lpbsFPHome) {
		char buf[MAX_PATH];
		// Get path to this module
		DWORD dw = ::GetModuleFileName(_Module.m_hInst, buf, MAX_PATH);
		buf[dw] = L'\0';
		// We only want directory portion
		char *p = strrchr(buf, L'\\');
		if(p) {
			*p = '\0';
			CComBSTR s = buf;
			*lpbsFPHome = s.Detach();
		} else
			hr = S_FALSE;
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CXFormsManager::GetFPHomeDirectory");
	}
	return hr;
}

// removes a form from the list of forms available.
void CXFormsManager::removeForm(long lFormKey)
{
	m_Forms.RemoveKey(reinterpret_cast<void*>(lFormKey));
}
/*
IConfigHandler* CXFormsManager::ConfigHandler(void)
{
	if(!m_pConfig) {
		IUnknown *pUnk = 0;
		if(SUCCEEDED(CoCreateInstance(CLSID_ConfigHandler, 0, CLSCTX_ALL, IID_IUnknown, reinterpret_cast<void**>(&pUnk)))) {
			m_pConfig = pUnk;
			pUnk->Release();
		}
	}
	return m_pConfig;
}
*/	/*

HRESULT CXFormsManager::InvokeSystray(CXFormsXFormsElement *pXFXFEl, BSTR sConfigFile, ISysTrayIcon **ppIcon)
{
	HRESULT hr = E_NOTIMPL;
	// Check the arguments.
	if(pXFXFEl && sConfigFile && ppIcon) {
		// First get a document pointer...
		CComPtr<IHTMLDocument2> pDoc;
		pXFXFEl->GetOwnerDocument(&pDoc);
		// ...then use that to get a pointer to the IWebBrowser2 interface.
		CComPtr<IWebBrowser2> pBrowser;
		hr = GetBrowserFromDocument(pDoc, &pBrowser);
		if(pBrowser) {
			// Now get a Soft-Systray object...
			IUnknown *pUnk = 0;
			xport::GetFirstImplementation(CComBSTR(L"ui.perceivableunit.systrayicon"), CComBSTR(L"1.0"), &pUnk, &m_pDIR.p);
			CComQIPtr<ISysTrayIcon> pSystray = pUnk;
			if(pSystray) {
				CComPtr<IUnknown> pUnkParam;
				pBrowser->QueryInterface(IID_IUnknown, reinterpret_cast<void **>(&pUnkParam));
				// ...and invoke it with our parameters.
				hr = pSystray->Show(sConfigFile, pUnkParam);
				// Return the icon to the caller.
				*ppIcon = pSystray.Detach();
			} else {
				hr = FP_E_NOCOM;
				SetupErrorInfo(hr, L"Soft-Systray");
			}
			if(pUnk)
				pUnk->Release();
		} else {
			hr = FP_E_BADARG;
			SetupErrorInfo(hr, L"XFormsManager::InvokeSystray");
		}
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"XFormsManager::InvokeSystray");
	}
	return hr;
}

HRESULT CXFormsManager::InvokeSystrayStrict(CXFormsXFormsElement *pXFXFEl, BSTR sMenu, BSTR sTitle, BSTR sIcon, long nWidth, long nHeight, bool bAnimate, BSTR sAnimateDirection, long nAnimateTime, ISysTrayIcon **ppIcon)
{
	HRESULT hr = E_NOTIMPL;
	// Check the arguments.
	if(pXFXFEl && sMenu && sTitle && nWidth > 0 && nHeight > 0 && ppIcon) {
		// First get a document pointer...
		CComPtr<IHTMLDocument2> pDoc;
		pXFXFEl->GetOwnerDocument(&pDoc);
		// ...then use that to get a pointer to the IWebBrowser2 interface.
		CComPtr<IWebBrowser2> pBrowser;
		hr = GetBrowserFromDocument(pDoc, &pBrowser);
		if(pBrowser) {
			// Now get a Soft-Systray object...
			IUnknown *pUnk = 0;
			xport::GetFirstImplementation(CComBSTR(L"ui.perceivableunit.systrayicon"), CComBSTR(L"1.0"), &pUnk, &m_pDIR.p);
			CComQIPtr<ISysTrayIcon> pSystray = pUnk;
			if(pSystray) {
				CComPtr<IUnknown> pUnkParam;
				pBrowser->QueryInterface(IID_IUnknown, reinterpret_cast<void **>(&pUnkParam));
				// ...and invoke it with our parameters.
				hr = pSystray->ShowStrict(sMenu, sTitle, sIcon, nWidth, nHeight, bAnimate ? VARIANT_TRUE : VARIANT_FALSE, sAnimateDirection, nAnimateTime, pUnkParam);
				// Return the icon to the caller.
				*ppIcon = pSystray.Detach();
			} else {
				hr = FP_E_NOCOM;
				SetupErrorInfo(hr, L"Soft-Systray");
			}
			if(pUnk)
				pUnk->Release();
		} else {
			hr = FP_E_BADARG;
			SetupErrorInfo(hr, L"XFormsManager::InvokeSystrayStrict");
		}
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"XFormsManager::InvokeSystrayStrict");
	}
	return hr;
}
*/
HRESULT CXFormsManager::GetBrowserFromDocument(IHTMLDocument2 *pDoc, IWebBrowser2 **ppWB2)
{
	HRESULT hr = S_OK;
	if(pDoc && ppWB2) {
		CComPtr<IHTMLWindow2> pWin;
		pDoc->get_parentWindow(&pWin);
		CComQIPtr<IServiceProvider> pSP = pWin;
		if(pSP)
			hr = pSP->QueryService(IID_IWebBrowserApp, IID_IWebBrowser2, (void**)ppWB2);
		else {
			hr = FP_E_QIFAIL;
			SetupErrorInfo(hr, L"IServiceProvider");
		}
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"XFormsManager::GetBrowserFromDocument");
	}
	return hr;
}
/*
// Return a pointer to the default system tray icon.
ISysTrayIcon *CXFormsManager::DefaultSystray(void)
{
	ISysTrayIcon *pRetval = 0;
	if(m_pDefaultSysTrayIcon)
		pRetval = m_pDefaultSysTrayIcon.p;
	return pRetval;
}
*/

#define CREATEDOMEVENTFACTORY_WITH_MESSAGELOOP 1
#undef CREATEDOMEVENTFACTORY_WITH_MESSAGELOOP
#ifdef CREATEDOMEVENTFACTORY_WITH_MESSAGELOOP


DWORD WINAPI CreateDomEventFactory(LPVOID pvdArg)
{
	HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    MSG msg;
	// force msg-q to be created just in case, NOP otherwise
	PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE | PM_NOYIELD);
	// msg to myself to do work
	PostThreadMessage(GetCurrentThreadId(), WM_USER+1, 0, 0);

	// msg-pump
	while (GetMessage(&msg, NULL, 0, 0))
	{
		// this was my message -- time to do my work
		if (msg.hwnd == NULL && msg.message == WM_USER+1)
		{
			IClassFactory ** ppFactory = reinterpret_cast<IClassFactory **>(pvdArg);
			hr = ::CoGetClassObject(CLSID_DOMEvent, CLSCTX_ALL, NULL, __uuidof(IClassFactory), (void **) ppFactory);
			SetEvent(hGlobalDomEventFactoryCreatedEvent);
			CloseHandle(hGlobalDomEventFactoryCreatedEvent);
			hGlobalDomEventFactoryCreatedEvent = 0;
	        PostQuitMessage(0);
			::CoUninitialize();
			ExitThread(0);
        }
	    else
        {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
        }
    }
	::CoUninitialize();
	ExitThread(0);
	return 0;	
}
#else
DWORD WINAPI CreateDomEventFactory(LPVOID pvdArg)
{
	HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
	IClassFactory ** ppFactory = reinterpret_cast<IClassFactory **>(pvdArg);
	hr = ::CoGetClassObject(CLSID_DOMEvent, CLSCTX_ALL, NULL, __uuidof(IClassFactory), (void **) ppFactory);
	SetEvent(hGlobalDomEventFactoryCreatedEvent);

	::CoUninitialize();
	ExitThread(0);
	return 0;	
}
#endif

/*
DWORD WINAPI ThreadInvokeDefaultSysTray(LPVOID pvdArg)
{
	HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
	CXFormsManager * pManager = reinterpret_cast<CXFormsManager *>(pvdArg);
	pManager->InvokeDefaultSysTray();
	ExitThread(0);
	return 0;	
}
*/
// Initialisation routine
HRESULT CXFormsManager::Initialise(BSTR /*sCurrentVersion*/)
{
	HRESULT hr = S_FALSE;
	if(!m_bInitialised) {
#ifndef FP_LEAN_AND_MEAN_DISABLE_CHECK_UPDATES
		if(SUCCEEDED(m_UpdateChecker.Initialise(sCurrentVersion)))
			m_UpdateChecker.CheckForUpdates();
#endif
		if(!m_pDIR)
			m_pDIR.CoCreateInstance(CLSID_DOMImplementationRegistry);

		if(!m_pDOMEventFactory)
		{
			hGlobalDomEventFactoryCreatedEvent = CreateEvent
			(
			        NULL,         // no security attributes
					TRUE,         // manual-reset event
					FALSE,         // initial state is unsignaled
					"CreateDOMEventFactoryEvent"  // object name
			);

#ifdef CREATE_DOMEVENTFACTORY_IN_NEW_THREAD
			DWORD dwIDThread;
			CreateThread(NULL, // no security attributes 
				0,                           // use default stack size 
				(LPTHREAD_START_ROUTINE) CreateDomEventFactory, // thread function 
				&m_pDOMEventFactory,                    // thread function argument 
				0,                       // use default creation flags 
				&dwIDThread);     
#else
			::CoGetClassObject(CLSID_DOMEvent, CLSCTX_ALL, NULL, __uuidof(IClassFactory), (void **) &m_pDOMEventFactory);
			SetEvent(hGlobalDomEventFactoryCreatedEvent);
#endif

		}
	/*	DWORD dwIDThread2;

		CreateThread(NULL, // no security attributes 
			0,                           // use default stack size 
			(LPTHREAD_START_ROUTINE) ThreadInvokeDefaultSysTray, // thread function 
			this,                    // thread function argument 
			0,                       // use default creation flags 
			&dwIDThread2);       

	//	hr = InvokeDefaultSysTray();
	*/	if(SUCCEEDED(hr))
			m_bInitialised = true;

	}
	return hr;
}

/*
// Increment system tray icon reference count.
HRESULT CXFormsManager::SystrayAddref(void)
{
	HRESULT hr = S_FALSE;

	m_nSystrayRefCount++;
	if(m_nSystrayRefCount > 0)
		hr = InvokeDefaultSysTray();

	return hr;
}

// Decrement system tray icon reference count.
HRESULT CXFormsManager::SystrayRelease(void)
{
	HRESULT hr = S_FALSE;

	m_nSystrayRefCount--;
	if(m_nSystrayRefCount <= 0) {
		hr = S_OK;
		m_pDefaultSysTrayIcon.Release();
	}

	return hr;
}

// Invoke the one-and-only default system tray icon.
HRESULT CXFormsManager::InvokeDefaultSysTray(void)
{
	HRESULT hr = S_OK;

#ifdef FP_LEAN_AND_MEAN_DISABLE_SYSTRAY
	return hr;
#endif

	// Only continue if the default Soft-SysTray has not already been invoked.
	if(!m_pDefaultSysTrayIcon) {
		// Set the default icon.
		CComBSTR sIcon = L"http://www.formsplayer.com/favicon.ico";
		// Set the default title.
		CComBSTR sTitle = L"formsPlayer";
		// Get the path to the default menu.
		CComBSTR sMenu = L"res://";
		char buf[MAX_PATH + 1];
		DWORD dw = ::GetModuleFileName(_Module.m_hInst, buf, MAX_PATH);
		buf[dw] = L'\0';
		sMenu += CA2W(buf);
		sMenu += L"/dfltstry.htm";
		// Invoke the Soft-SysTray.
		CComPtr<IUnknown> pUnk;
		xport::GetFirstImplementation(CComBSTR(L"ui.perceivableunit.systrayicon"), CComBSTR(L"1.0"), &pUnk, &m_pDIR.p);
		m_pDefaultSysTrayIcon = pUnk;
		if(m_pDefaultSysTrayIcon)
			hr = m_pDefaultSysTrayIcon->ShowStrict(sMenu, sTitle, 0, 236, 200, VARIANT_TRUE, CComBSTR(L"ne"), 200, 0);
		else {
			hr = FP_E_NOITF;
			SetupErrorInfo(hr, L"ISysTrayIcon");
		}
		if(pUnk)
			pUnk.Release();
	} else
		hr = S_FALSE;
	return hr;
}
*/


#define WAIT_FOR_DOMEVENT_WITH_MESSAGELOOP 1
#undef WAIT_FOR_DOMEVENT_WITH_MESSAGELOOP
HRESULT CXFormsManager::CreateDOMEvent(IDOMEvent** ppEvent)
{
	HRESULT hr;
	DWORD dwWaitResult = WAIT_OBJECT_0; 
	// Await the creation event
	if(hGlobalDomEventFactoryCreatedEvent)
	{

#ifdef WAIT_FOR_DOMEVENT_WITH_MESSAGELOOP
		dwWaitResult = xport::WaitWithMessageLoop(hGlobalDomEventFactoryCreatedEvent,INFINITE);
#else
		dwWaitResult = ::WaitForSingleObject(hGlobalDomEventFactoryCreatedEvent,INFINITE);
#endif
		CloseHandle(hGlobalDomEventFactoryCreatedEvent);
		hGlobalDomEventFactoryCreatedEvent = 0;
	}
	else if(!m_pDOMEventFactory)
	{
		::CoGetClassObject(CLSID_DOMEvent, CLSCTX_ALL, NULL, __uuidof(IClassFactory), (void **) &m_pDOMEventFactory);
	}
/*
	dwWaitResult = xport::WaitWithMessageLoop( 
			hGlobalDomEventFactoryCreatedEvent,   // handle to semaphore
			INFINITE);          // infinite time-out interval
*/
#ifdef WAIT_FOR_DOMEVENT_WITH_MESSAGELOOP
	if(m_pDOMEventFactory)
	{
		hr = m_pDOMEventFactory->CreateInstance(NULL, __uuidof(IDOMEvent), (void **) ppEvent);
	}
	else
	{
		hr = E_FAIL;
		ASSERT(m_pDOMEventFactory);
	}
#else
	switch (dwWaitResult) { 

		// The semaphore object was signaled.
		case WAIT_OBJECT_0: 
			hr = m_pDOMEventFactory->CreateInstance(NULL, __uuidof(IDOMEvent), (void **) ppEvent);
			break; 

		// Semaphore was nonsignaled, so a time-out occurred.
		case WAIT_TIMEOUT: 
			hr = E_FAIL;
			break; 
	}
#endif
	return hr;
	
}
